//
//  main.c
//  SC2
//
//  Created by Craig Markham and James Marquardt on 1/15/11.
//  Copyright (c) 2011 University of Washington at Tacoma. All rights reserved.
//

#include "main.h"

/**
 * main
*/
int main() {
	run_debug();

	//uncomment test to be performed
//	test_register();
//	test_registerfile();
//	test_alu();
//	test_memory();
//	test_cpu();

  return 0;
}

/**
 * Run the Debug Monitor
*/
void run_debug(){

	int shouldRun = TRUE;
	int address = 0;
	CPU * CPU_ptr = initialize(CPU_ptr);

    while (shouldRun) {
        showAll(CPU_ptr);
        int option = mainMenu();

        switch (option) {
            case MENU_LOAD:
            	loadProgram(CPU_ptr);
                break;

            case MENU_RUN:
                runProgram(CPU_ptr);
                break;

            case MENU_SET_STEP:
            	setBreakpoint(CPU_ptr);
                break;

            case MENU_STEP:
            	stepProgram(CPU_ptr);
                break;

            case MENU_SHOW_MEM:

                printf("\nEnter starting address of memory in hex to view: ");
            	scanf("%x", &address);

            	showMemory(CPU_ptr, address);
                break;

            case MENU_SHOW_REG:
            	showRegisters(CPU_ptr);
                break;

            case MENU_EXIT:
                shouldRun = FALSE;
                break;

            default:
                break;
        }
    }
}

/**
 * testing for Register functions
*/
void test_register() {
  uchar init_byte, final_byte;
  ushort init_word, final_word;
  printf("Test SC2_register\n");

  RegisterPtr reg_ptr = 0;
  int error = 0, test = 0;

//Test Basic Functionality

  * reg_ptr = (int) malloc (sizeof(ushort));
  register_init(reg_ptr);
  printf("Test: register_init:%x\n", *reg_ptr);

  //Test Set and put Reg Value
  init_word = 0xabcd;
  error = register_putRegValue(reg_ptr, init_word);
  final_word = register_getRegValue(reg_ptr, error);
//  printushort(register_getRegValue(reg_ptr, * error));

  if(init_word == final_word)
  printf("Test Passed: register_putRegValue/register_getRegValue\n");
  else
  printf("Test Failed: register_putRegValue/register_getRegValue\n");

  //Test Get and Put LOB Value
  init_word = 0x1234;
  error = register_putRegValue(reg_ptr, init_word);//Reset Register Value
  init_byte = 0xef;
  error = register_putLowByte(reg_ptr, init_byte);
  final_word = register_getRegValue(reg_ptr, error);
  if(final_word == 0x12ef)
  printf("Test Passed: register_putLowByte\n");
  else
  printf("Test Failed: register_putLowByte\n");

  final_byte = register_getLowByte(reg_ptr, &error);
  if(init_byte == final_byte)
  printf("Test Passed: register_getLowByte\n");
  else
  printf("Test Failed: register_getLowByte\n");

  //Test Get and Put HOB Value
  init_word = 0x5678;
  error = register_putRegValue(reg_ptr, init_word);//Reset Register Value
  init_byte = 0x9a;
  error = register_putHighByte(reg_ptr, init_byte);
  final_word = register_getRegValue(reg_ptr, error);
  if(final_word == 0x9a78)
  printf("Test Passed: register_putHighByte\n");
  else
  printf("Test Failed: register_putHighByte\n");

  final_byte = register_getHighByte(reg_ptr, &error);
  if(init_byte == final_byte)
  printf("Test Passed: register_getHighByte\n");
  else
  printf("Test Failed: register_getHighByte\n");

//Test signOf(+)
  register_putRegValue(reg_ptr, 1024);//set Register to positive value
  test = register_signOf(reg_ptr);

  if(test == 0)
  printf("Test Passed: signOf(+)\n");
  else
  printf("Test Failed: signOf(+)\n");

  //Test signOf(-)
  register_putRegValue(reg_ptr, -512);//set Register to negative value
  test = register_signOf(reg_ptr);

  if(test == 1)
  printf("Test Passed: signOf(-)\n");
  else
  printf("Test Failed: signOf(-)\n");
}

/**
 * testing for Register File functions
*/
void test_registerfile() {
  RegisterFilePtr rf;
  int error;
  ushort valshort = 0, tempshort = 0;
  uchar valchar, tempchar;
  printf("\nTest registerfile:\n");
  rf = registerFile_initialize();

//Test get and put Reg Value
  tempshort = 0xabcd;
  valshort = 0;
  error = registerFile_putRegValue(rf, 5, tempshort);
  valshort = registerFile_getRegValue(rf, 5, &error);
  if(tempshort == valshort)
  printf("Test Passed: registerFile_putRegValue/registerFile_getRegValue\n");
  else
  printf("Test Failed: registerFile_putRegValue/registerFile_getRegValue\n");

//Test get and put LOB byte Value
  tempchar = 0x12;
  valchar = 0;
  error = registerFile_putByteTo(rf, 5, 0, tempchar);
  valchar = registerFile_getByteFrom(rf, 5, 0, &error);
  if(tempchar == valchar)
  printf("Test Passed: registerFile_putByteTo/registerFile_getByteFrom LOB\n");
  else
  printf("Test Failed: registerFile_putByteTo/registerFile_getByteFrom LOB\n");

//Test get and put HOB byte Value
  tempchar = 0x34;
  valchar = 0;
  error = registerFile_putByteTo(rf, 5, 1, tempchar);
  valchar = registerFile_getByteFrom(rf, 5, 1, &error);
  if(tempchar == valchar)
  printf("Test Passed: registerFile_putByteTo/registerFile_getByteFrom HOB\n");
  else
  printf("Test Failed: registerFile_putByteTo/registerFile_getByteFrom HOB\n");
}

/**
 * A test of the ALU
*/
void test_alu() {

  ALU * alu_ptr = 0;

  int error = 1;

  alu_ptr = alu_init(alu_ptr);

  printf("\nTesting ALU");

  //Loads random operands into operand registers.

  printf("\n\nLoading ALU Register A");
  register_putRegValue(&alu_ptr->register_a, rand() % 0xFFFF);

  printf("\nLoading ALU Register B");
  register_putRegValue(&alu_ptr->register_b, rand() % 0xFFFF);

  printf("\n\nThe contents of Register A: %x",
    register_getRegValue(&alu_ptr->register_a, error));

  printf("\nThe contents of Register B: %x",
    register_getRegValue(&alu_ptr->register_b, error));

//Tests for all possible ALU operations.
  //Tests ADD operations.
  printf("\n\nTesting ADD Operation");
  alu_getOperation(alu_ptr, OVERLOAD_OP, ADD_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (ADD).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests SUB operations.
  printf("\n\nTesting SUB Operation");
  alu_getOperation(alu_ptr, OVERLOAD_OP, SUB_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (SUB).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests MUL operations.
  printf("\n\nTesting MUL Operation");
  alu_getOperation(alu_ptr, OVERLOAD_OP, MUL_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (MUL).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests DIV operations.
  printf("\n\nTesting DIV Operation");
  alu_getOperation(alu_ptr, OVERLOAD_OP, DIV_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (DIV).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests NOT operations.
  printf("\n\nTesting NOT Operation");
  alu_getOperation(alu_ptr, OVERLOAD_OP, NOT_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (NOT).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests AND operations.
  printf("\n\nTesting AND Operation");
  alu_getOperation(alu_ptr, OVERLOAD_OP, AND_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (AND).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests OR operations.
  printf("\n\nTesting OR Operation");
  alu_getOperation(alu_ptr, OVERLOAD_OP, OR_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (OR).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests XOR operations.
  printf("\n\nTesting XOR Operation");
  alu_getOperation(alu_ptr, OVERLOAD_OP, XOR_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (XOR).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests SHL operations.
  printf("\n\nTesting SHL Operation");
  alu_getOperation(alu_ptr, SHL_OP, NOT_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (SHL).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));

  //Tests SHR operations.
  printf("\n\nTesting SHR Operation");
  alu_getOperation(alu_ptr, SHR_OP, NOT_OP);

  printf("\nThe contents of the Primary Op: %d",
    alu_ptr->primary_op);

  printf("\nThe contents of the Overload Op: %d",
    alu_ptr->overload_op);

  printf("\nPerforming operation (SHR).");

  alu_perform_operation_primary(alu_ptr, &error);

  printf("\nThe contents of Result Register: %x",
    register_getRegValue(&alu_ptr->register_r, error));
}

/**
 * testing for Memory Module functions
*/
void test_memory(){
  int i = 0;
  int error = -1;
  uchar init_byte, final_byte;
  ushort init_word, final_word;
  printf("\nTest SC2_memory\n");
  MemoryModulePtr mm = memory_initialize();

//Test Basic Functionality
  //Test store and fetch byte
  init_byte = 0xAB;
  error = memory_storeByteTo(mm, 0x3000, init_byte);
  final_byte = memory_fetchByteFrom(mm, 0x3000, &error);

  if(init_byte == final_byte)
    printf("Test Passed: memory_storeByteTo/memory_fetchByteFrom\n");
  else
    printf("Test Failed: memory_storeByteTo/memory_fetchByteFrom\n");

//Test store and fetch word
  init_word = 0x9876;
  error = memory_storeWordTo(mm, 0x3000, init_word);
  final_word = memory_fetchWordFrom(mm, 0x3000, &error);
  if(init_word == final_word)
    printf("Test Passed: memory_storeWordTo/memory_fetchWordFrom\n");
  else
    printf("Test Failed: memory_storeWordTo/memory_fetchWordFrom\n");

  // Test Full Memory Read
  for (i=0; i < MEMORY_SIZE; i++){
	  printf("\nmem address: 0x%x; Mem Value: 0x%x",  0x3000 + i,
			  memory_fetchByteFrom(mm, 0x3000 + i , &error));
  }

//Test Error code setting

}

/*
 * testing for CPU functions
 */
void test_cpu(){
	int i, error;
	CPU * CPU_ptr = 0;
	CPU_ptr = initialize(CPU_ptr);

	  // Test Full Memory Read
	  for (i=0; i < MEMORY_SIZE; i++){
		  printf("\nmem address: 0x%x; Mem Value: 0x%x",  0x3000 + i,
				  memory_fetchByteFrom(CPU_ptr->mainMemory, 0x3000 + i , &error));
	  }

}

/* Print uchar n as a binary number */
void printuchar(uchar n) {
  uchar i;
  i = 1<<(sizeof(n) * 8 - 1);
  while (i > 0) {
    if (n & i)
      printf("1");
    else
      printf("0");
      i >>= 1;
  }
  printf("\n");
}

/* Print ushort n as a binary number */
void printushort(ushort n) {
  ushort i;
  i = 1<<(sizeof(n) * 8 - 1);
  while (i > 0) {
    if (n & i)
      printf("1");
    else
      printf("0");
    i >>= 1;
  }
  printf("\n");
}
